require "class"
local Event = require "xs_shortcut_key_event"


local ShortcutKeys = Class(function(self)
    -- 按键监听(可使用的按键)
    self.keys = {}
    -- 活跃按键(当前按下的按键)
    self.active = {}
    -- 组合起始键(组合键必须以这些按键为开头)
    self.combination_start = {}

    self.period = 0   -- 执行间隔时间(ms)
    self.is_c = false -- 组合键模式

    -- 各类事件
    self.events = {}

    for key, key_id in pairs(XS_PressKeyMaps) do
        self.keys[key] = {
            id = key_id, -- 按键ID
            down = nil,  -- 按键按下监听程序
        }
    end
end)

-- 添加组合键起始键
function ShortcutKeys:AddCSKeys(keys)
    if type(keys) == "string" and self.keys[keys] then
        self.combination_start[keys] = true
    elseif type(keys) == "table" then
        for _, key in pairs(keys) do
            if self.keys[key] then
                self.combination_start[key] = true
            end
        end
    end
end

-- 移除组合键起始键
function ShortcutKeys:RemoveCSKeys(keys)
    if type(keys) == "table" then
        for _, key in pairs(keys) do
            self.combination_start[key] = nil
        end
    else
        self.combination_start[keys] = nil
    end
end

-- 获取当前按下按键
function ShortcutKeys:GetActiveKeys()
    return table.concat_v(self.active, self.combination_sep)
end

-- 组合键处理
function ShortcutKeys:CombinationKeys(keys)
    -- 单键
    if type(keys) == "string" then
        keys = string.upper(keys)
        return self.keys[keys]
    end

    -- 单/多键
    if table.size(keys) > 0 then
        for k, key in pairs(keys) do
            if not type(key) == "string" then
                return
            end
            key = string.upper(key)
            if not self.keys[key] then
                return
            end
            keys[k] = key
        end

        return table.concat_v(keys, self.combination_sep)
    end
end

-- 刷新保持时间
function ShortcutKeys:UpdateKeep(reset)
    local current_clock = os.clock()
    if reset then
        self.clock = current_clock
    end
    self.keep = current_clock - self.clock
end

-- 移除事件
function ShortcutKeys:RemoveEvent(keys, meta, fn)
    keys = self:GetCombinationKey(keys)

    if keys then
        local events = meta[keys]

        if type(events) == "table" then
            for i, event in pairs(events) do
                if event and event.fn == fn then
                    table.remove(events, i)
                    break
                end
            end

            if table.size(events) == 0 then
                meta[keys] = nil
            end
        end
    end
end

-- 监听任务, 按下持续中触发
function ShortcutKeys:AddEventKeepOn()

end

-- 添加按键按下持续结束后触发事件, 按键, 事件处理函数, 触发所需最短时长, 实际触发时长误差, 事件处理函数额外传入数据
function ShortcutKeys:AddEventKeepOff(key, fn, keep, interval, data)
    if type(key) == "string" and type(fn) == "function" and type(keep) == "number" and keep > 0 then
        key = string.upper(key)
        if self.keys[key] then
            if not self.keep_off[key] then
                self.keep_off[key] = {}
            end

            local event = { fn = fn, keep = keep, interval = 1, data = data }
            -- 误差间隔, 如持续5秒可触发4~5秒的事件
            if type(interval) == "number" and interval > 0 then
                event.interval = interval
            end

            table.insert(self.keep_off[key], event)
        end
    end
end

-- 移除按键按下持续结束后触发事件, 按键, 事件处理函数
function ShortcutKeys:RemoveEventKeepOff(key, fn)
    if type(key) == "string" then
        self:RemoveEvent(key, self.keep_off, fn)
    end
end

-- 添加按键按下事件, 按键/组合键(按键表)
function ShortcutKeys:AddEventDown(keys, fn, data)
    keys = self:GetCombinationKey(keys)

    if keys and type(fn) == "function" then
        if not self.down[keys] then
            self.down[keys] = {}
        end

        table.insert(self.down[keys], { fn = fn, data = data })
    end
end

-- 移除按键按下事件
function ShortcutKeys:RemoveEventDown(keys, fn)
    self:RemoveEvent(keys, self.down, fn)
end

-- 添加按键按下持续触发事件
function ShortcutKeys:AddEventDownOn(keys, fn, interval, limit, data)
    keys = self:GetCombinationKey(keys)

    if keys and type(fn) == "function" then
        if not self.down_on[keys] then
            self.down_on[keys] = {}
        end

        local event = { keys = keys, fn = fn, interval = 1, data = data }
        -- 持续触发间隔, 默认1秒
        if type(interval) == "number" and interval > 0 then
            event.interval = interval
        end
        -- 持续触发限制次数, 默认不限制
        if type(limit) == "number" and limit > 0 then
            event.limit = math.ceil(limit)
        end

        table.insert(self.down_on[keys], event)
    end
end

-- 移除按键按下持续触发事件
function ShortcutKeys:RemoveEventDownOn(keys, fn)
    self:RemoveEvent(keys, self.down_on, fn)
end

-- 添加按键放开事件
function ShortcutKeys:AddEventUp(key, fn, data)
    if type(key) == "string" and type(fn) == "function" then
        key = string.upper(key)

        if self.keys[key] then
            if not self.up[key] then
                self.up[key] = {}
            end

            table.insert(self.up[key], {
                fn = fn,     -- 事件回调函数
                data = data, -- 事件函数数据
            })
        end
    end
end

-- 移除按键放开事件
function ShortcutKeys:RemoveEventUp(key, fn)
    if type(key) == "string" then
        self:RemoveEvent(key, self.up, fn)
    end
end

-- 定时任务
function ShortcutKeys:AddTask()
    self.task = scheduler:ExecutePeriodic(self.interval, function(this)
        this:UpdateKeep()

        -- 按下按键持续中触发
        local keys = this:GetKeys()
        if this.keep_on[keys] then
            for _, event in pairs(this.keep_on[keys]) do
                if event.active and this.keep >= event.keep then
                    event.active = false
                    event.fn(event.data)
                end
            end
        end
    end, nil, 0, 0, self)
end

-- 定时任务取消
function ShortcutKeys:RemoveTask()
    if self.task then
        self.task:Cancel()
        self.task = nil

        for _, events in pairs(self.keep_on) do
            for _, event in pairs(events) do
                event.active = true -- 刷新可触发状态
            end
        end
    end
end

-- 定时任务重置
function ShortcutKeys:ResetTask()
    self:RemoveTask()
    self:AddTask()
end

-- 按键按下, 按键
function ShortcutKeys:KeyDown(key)
    table.insert(self.active, key)

    self:UpdateKeep(true)
    local keys = self:GetActiveKeys()

    -- 按下按键触发
    if self.down[keys] then
        for _, event in pairs(self.down[keys]) do
            event.fn(event.data)
        end
    end

    -- 按下按键持续触发
    if self.down_on[keys] then
        for _, event in pairs(self.down_on[keys]) do
            event.handler = scheduler:ExecutePeriodic(event.interval, function(this, event_)
                if this:GetKeys() == event_.keys then
                    event_.fn(event_.data)
                else
                    event_.handler:Cancel()
                    event_.handler = nil
                end
            end, event.limit, 0, 0, self, event)
        end
    end

    self:AddTask()
    self:RemoveKeyDown(key_name)
end

-- 按键按下监听
function ShortcutKeys:AddKeyDown(key)
    if self.keys[key] then
        self.keys[key].down = TheInput:AddKeyDownHandler(self.keys[key].id, function()
            self:KeyDown(key)
        end)
    end
end

-- 移除按键按下监听
function ShortcutKeys:RemoveKeyDown(key_name)
    self.keys[key_name].down:Remove()
end

-- 恢复按键按下监听
function ShortcutKeys:ResumptionKeyDown(key_name)
    if self.keys[key_name] and self.keys[key_name].down then
        table.insert(TheInput.onkeydown.events, self.keys[key_name].down)
    else
        self:AddKeyDown(key_name)
    end
end

-- 按键放开处理
function ShortcutKeys:KeyUp(key)
    --region 解除按键按下持续触发, 防止多重触发
    local keys = self:GetActiveKeys()
    if keys and self.down_on[keys] then
        for _, event in pairs(self.down_on[keys]) do
            if event.handler then
                event.handler:Cancel()
                event.handler = nil
            end
        end
    end
    --endregion 解除按键按下持续触发, 防止多重触发

    --region 放开触发事件
    if table.size(self.active) == 1 then
        self:UpdateKeep()

        -- 按下按键持续结束后触发
        if self.keep_off[key] then
            for _, event in pairs(self.keep_off[key]) do
                local dt = self.keep - event.keep
                if dt > 0 and dt <= event.interval then
                    event.fn(event.data)
                end
            end
        end

        -- 放开按键触发
        if self.up[key] then
            for _, event in pairs(self.up[key]) do
                event.fn(event.data)
            end
        end
    end
    --endregion 放开触发事件

    --region 放开按键处理
    table.remove_v(self.active, key)
    self:UpdateKeep(true)
    self:ResumptionKeyDown(key)

    if table.size(self.active) > 0 then
        self:ResetTask()
    else
        self:RemoveTask()
    end
    --endregion 放开按键处理
end

-- 按键放开监听
function ShortcutKeys:AddKeyUp(key)
    self.keys[key].up = TheInput:AddKeyUpHandler(self.keys[key].id, function()
        self:KeyUp(key)
    end)
end
